#pragma once

enum eBuildAction
{
	BUILD_PARSE,
	BUILD_COMPILE,
	BUILD_ASSEMBLE,
	BUILD_COPY,
	BUILD_RUNEXE,
	BUILD_IGNORE,
};
enum eBuildItemType
{
	ITEM_NONE,
	ITEM_SCRIPT,
	ITEM_SUBSCRIPT,
	ITEM_INCLUDE,
};
enum eBuildEvent
{
	PARSE,
	COMPILE,
	PARSE_SUBSCRIPT,
	INCLUDE_FILE,
	PROJECT_BUILD,
};
enum eEventOrder
{
	BEFORE,
	AFTER,
	DURING,
};
enum eEventVars
{
	EVENT_VAR_SCRIPT,
	EVENT_VAR_SUBSCRIPT,
	EVENT_VAR_SCRIPTFILE,
	EVENT_VAR_PROJECT,
};

class CBuildItem
{
	eBuildItemType		m_eType;
	void			*	m_pItem;

public:
template<typename T>
	CBuildItem(eBuildItemType eType, T * pItem)	:	m_eType(eType),
													m_pItem((void*)pItem)
	{
	}
};

class CBuildStep
{
	eBuildAction		m_eType;
	CBuildItem		*	m_pItem;

public:
template<typename T>
	CBuildStep(eBuildAction eType, CBuildItem * pItem)	:	m_eType(eType),
															m_pItem(pItem)
	{
	}

	void SetItem(CBuildItem	* pItem)
	{}
};

class CBuilder
{
	CLListEx<CBuildStep*>		m_BuildSteps;
};

class CBuildEvent : public CLList<CBuildEvent>
{
	friend class CBuildEventIndex;
	friend class CBuildEventInternal;

	call			m_pFunc;

	CBuildEvent()				: m_pFunc(nullptr)
	{
	}
	CBuildEvent(void(*pFunc)()) : m_pFunc(pFunc)
	{
	}

	void			TriggerBefore()
	{
		auto pEvent = Prev();
		if(pEvent)
		{
			do if(pEvent->Function<void(*)()>()) pEvent->Function<void(*)()>()();
			while(pEvent = pEvent->Prev());
		}
	}

	void			TriggerAfter()
	{
		auto pEvent = Next();
		if(pEvent)
		{
			do if(pEvent->Function<void(*)()>()) pEvent->Function<void(*)()>()();
			while(pEvent = pEvent->Next());
		}
	}

	template<typename T>
	T				Function()			{return (T)m_pFunc;}
};

class CBuildEventInternal : CBuildEvent
{
	friend class CBuildEvents;
	CLListEx<CBuildEvent*>	Simultaneous;

	CBuildEventInternal() : CBuildEvent(), Before(BEFORE, this), During(DURING, this), After(AFTER, this)
	{
	}

	void Add(eEventOrder eEventOrder)
	{
		switch(eEventOrder)
		{
		case BEFORE:
			Prev(new CBuildEvent());
			break;
		case AFTER:
			Next(new CBuildEvent());
			break;
		case DURING:
			Simultaneous.Add(new CBuildEvent());
			break;
		}
	}

public:
	class CBuildEventIndex
	{
		friend class CBuildEventInternal;
		eEventOrder					m_eOrder;
		CBuildEvent				*	m_pBuildEvent;

		CBuildEventIndex(eEventOrder eOrder, CBuildEventInternal * pEvent) : m_eOrder(eOrder), m_pBuildEvent(pEvent)
		{
		}

	public:
		void	operator()() const
		{
			switch(m_eOrder)
			{
			case BEFORE:
				m_pBuildEvent->TriggerBefore();
				break;
			case AFTER:
				m_pBuildEvent->TriggerAfter();
				break;
			}
		}
		const CBuildEvent	&	operator=(void(*pFunc)()) const
		{
			CBuildEvent * pEvent = nullptr;
			switch(m_eOrder)
			{
			case BEFORE:
				m_pBuildEvent->Prev(pEvent = new CBuildEvent(pFunc));
				break;
			case AFTER:
				m_pBuildEvent->Next(pEvent = new CBuildEvent(pFunc));
				break;
			case DURING:
				//m_pBuildEvent->Simultaneous.Add(&pEvent);
				break;
			}
			return *pEvent;
		}
	} Before, After, During;

	const CBuildEventIndex	&	operator[](eEventOrder eOrder) const
	{
		switch(eOrder)
		{
		default:
		case BEFORE: return Before;
		case AFTER: return After;
		case DURING: return During;
		}
	}
	void	operator=(void(*pFunc)())
	{
		if(m_pFunc) throw(SCRambl.GetText("GENERAL","M105"));
		m_pFunc = pFunc;
	}
};

class CBuildEvents
{
	CBuildEventInternal Parse,
						Compile,
						ParseSubScript,
						IncludeFile;

public:
	CBuildEvents();

	const CBuildEventInternal	&	operator[](eBuildEvent eEvent)
	{
		switch(eEvent)
		{
		case PARSE: return Parse;
		case COMPILE: return Compile;
		case PARSE_SUBSCRIPT: return ParseSubScript;
		case INCLUDE_FILE: return IncludeFile;
		}
		Exception(SCRambl.GetText("GENERAL", "M104"));
		return Parse;
	}
};

extern CBuilder			Builder;
extern CBuildEvents		BuildEvents;